package matchbox;

import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;

import inference.Graph;
import inference.MessagePassing;

import org.nicta.social.MovieLens;
import org.nicta.social.MovieLensMF;

import utils.Printer;

import basics.DenseMatrix;
import basics.DenseVector;
import basics.FeaturesSet;
import bayesian.SimpleBayesian;
import bayesian.SimpleBayesianSampling;

public class Handler {

	public static void run() {
		runMovieLense();

		// runDummy();
	}

	private static void runDummy() {
		FeaturesSet X = new FeaturesSet();
		X.read("./testData/dummy/X.csv");

		FeaturesSet Y = new FeaturesSet();
		Y.read("./testData/dummy/Y.csv");

		FeaturesSet R = new FeaturesSet();
		R.read("./testData/dummy/R.csv");

		// Matchbox.trainEval(X, Y, R, 5);
	}

	private static void runMovieLense() {
		MovieLens m = new MovieLens() {
		};

		try {
			Classifier classifier = new Matchbox();

			PrintStream ps = new PrintStream("./log/results_" + classifier.getClass().getName() + ".txt");
			HashMap<Integer, Double[]> users = m.getBinaryUserFeatures();
			HashMap<Integer, Double[]> items = m.getMovieFeatures();
			HashMap<Integer[], Double> ratings = m.getRatings();

			SimpleDateFormat formatter = new SimpleDateFormat("E yyyy.MM.dd 'at' hh:mm:ss a zzz");

			// for (int train_size = 100; train_size < (int) (.9 * users.size()); train_size += 50) {
			int train_size = 700;
			// int train_size = 300; // (int) (.9 * users.size()); //
			int test_size = Math.min(100, users.size() - train_size); // users.size() - train_size; //

			int indx = 0;

			HashMap<Integer, HashMap<Integer, Double>> pmf_ratings = new HashMap<Integer, HashMap<Integer, Double>>();
			HashMap<Integer, HashSet<Integer>> pmf_user = new HashMap<Integer, HashSet<Integer>>();
			HashMap<Integer[], Double> pmf_test = new HashMap<Integer[], Double>();

			FeaturesSet R = new FeaturesSet();
			FeaturesSet X = new FeaturesSet();
			FeaturesSet Y = new FeaturesSet();

			FeaturesSet R_eval = new FeaturesSet();
			FeaturesSet X_eval = new FeaturesSet();

			DenseVector ids = new DenseVector(users.size());
			ArrayList<Integer> visited_ids = new ArrayList<Integer>();

			for (Integer key : users.keySet()) {
				Double[] d = users.get(key);
				DenseVector x = new DenseVector(d);
				ids.set(0);
				if (!visited_ids.contains(key)) {
					visited_ids.add(key);
				}
				ids.set(1, visited_ids.indexOf(key));
				x = (DenseVector) x.concat(ids);
				if (indx++ < train_size) {
					X.add(x);
					R.add(new DenseVector(items.size()));
				} else if (indx++ < train_size + test_size) {
					X_eval.add(x);
					R_eval.add(new DenseVector(items.size()));
				} else
					break;
			}

			visited_ids.clear();
			ids = new DenseVector(items.size());
			for (Integer key : items.keySet()) {
				Double[] d = items.get(key);
				DenseVector x = new DenseVector(d);
				ids.set(0);
				if (!visited_ids.contains(key)) {
					visited_ids.add(key);
				}
				ids.set(1, visited_ids.indexOf(key));
				x = (DenseVector) x.concat(ids);

				Y.add(x);
			}

			for (Integer[] ui : ratings.keySet()) {
				if (ui[0] - 1 < R.size()) {
					R.get(ui[0] - 1).set(ratings.get(ui), ui[1] - 1);
				} else if (ui[0] - R.size() < R_eval.size()) {
					R_eval.get(ui[0] - R.size()).set(ratings.get(ui), ui[1] - 1);
					pmf_test.put(ui, ratings.get(ui));
				} else {
					continue;
				}

				if (!pmf_ratings.containsKey(ui[1])) {
					pmf_ratings.put(ui[1], new HashMap<Integer, Double>());
				}
				if (!pmf_user.containsKey(ui[0])) {
					pmf_user.put(ui[0], new HashSet<Integer>());
				}

				pmf_ratings.get(ui[1]).put(ui[0], ratings.get(ui)); // movie_id, user_id, rating
				pmf_user.get(ui[0]).add(ui[1]);

			}

			// for (int k = 5; k <= 30; k += 5) {
			int k = 5;
			MovieLensMF pmf = new MovieLensMF(k);
			Printer.print(ps, "Training set: " + train_size + ", Test set: " + test_size + ", k: " + k + "\n");
			Printer.print(ps, "Started at: " + formatter.format(Calendar.getInstance().getTime()) + "\n");
			// Printer.print(ps, "PMF_MAE: " + pmf.predict(pmf_ratings, pmf_user, pmf_test) + "\n");
			Printer.print(ps, Classifier.trainEval(classifier, X, Y, R, X_eval, Y, R_eval, k) + "\n");
			// Printer.print(ps, Matchbox.trainEval(X, Y, R, X_eval, Y, R_eval, k) + "\n"); // (X, Y, 10);
			// Printer.print(ps, SimpleBayesian.trainEval(X, Y, R, X_eval, Y, R_eval, k) + "\n"); // (X, Y, 10);
			Printer.print(ps, "Ended at: " + formatter.format(Calendar.getInstance().getTime()) + "\n\n");
			ps.flush();
			// }
			// }
			ps.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	// private static void shuffle(HashMap<Integer, Double[]> col) {
	// Integer[] a = col.keySet().toArray(new Integer[] {});
	// for (int i = 0; i < a.length; i++) {
	// Double[] d = col.get(a[i]);
	// Random r = new Random();
	// int idx = r.nextInt(a.length);
	// col.put(a[idx], col.get(a[idx]));
	// col.put(a[i], d);
	// }
	// }
}
